<?php

namespace maotool\http;

use maotool\exception\Exception;
use maotool\exception\HttpException;

/**
 * HTTP请求构建器类
 * 提供链式调用方式构建和执行HTTP POST请求
 */
class HttpRequest
{
    /** @var string 请求URL地址 */
    private $url;

    /** @var array 请求头信息 */
    private $headers = array();

    /** @var array 表单参数 */
    private $formParams = array();

    /** @var int 超时时间(毫秒) */
    private $timeoutMs = 5000;

    /** @var string HTTP请求方法 */
    private $method = 'POST';

    /** @var mixed JSON数据 */
    private $jsonBody = null;


    /**
     * 构造函数，私有化防止直接实例化
     *
     * @param string $url 请求URL地址
     */
    private function __construct($url)
    {
        $this->url = $url;
    }

    /**
     * 创建POST请求实例
     *
     * @param string $url 请求URL地址
     * @return HttpRequest 返回HttpRequest实例
     */
    public static function post($url)
    {
        $instance = new self($url);
        $instance->method = 'POST';
        return $instance;
    }

    /**
     * 创建GET请求实例
     *
     * @param string $url 请求URL地址
     * @return HttpRequest 返回HttpRequest实例
     */
    public static function get($url)
    {
        $instance = new self($url);
        $instance->method = 'GET';
        return $instance;
    }

    /**
     * 创建PUT请求实例
     *
     * @param string $url 请求URL地址
     * @return HttpRequest 返回HttpRequest实例
     */
    public static function put($url)
    {
        $instance = new self($url);
        $instance->method = 'PUT';
        return $instance;
    }

    /**
     * 创建DELETE请求实例
     *
     * @param string $url 请求URL地址
     * @return HttpRequest 返回HttpRequest实例
     */
    public static function delete($url)
    {
        $instance = new self($url);
        $instance->method = 'DELETE';
        return $instance;
    }

    /**
     * 创建PATCH请求实例
     *
     * @param string $url 请求URL地址
     * @return HttpRequest 返回HttpRequest实例
     */
    public static function patch($url)
    {
        $instance = new self($url);
        $instance->method = 'PATCH';
        return $instance;
    }

    /**
     * 设置请求头信息
     *
     * @param string $key 请求头键名
     * @param string $value 请求头值
     * @return HttpRequest 返回当前实例以支持链式调用
     */
    public function header($key, $value)
    {
        $this->headers[$key] = $value;
        return $this;
    }

    /**
     * 设置表单参数
     *
     * @param array $params 表单参数数组
     * @return HttpRequest 返回当前实例以支持链式调用
     */
    public function form($params)
    {
        $this->formParams = $params;
        $this->header("Content-Type", "application/x-www-form-urlencoded");
        return $this;
    }

    /**
     * 设置JSON请求体
     *
     * @param mixed $data JSON数据
     * @return HttpRequest 返回当前实例以支持链式调用
     */
    public function json($data)
    {
        $this->jsonBody = $data;
        $this->header("Content-Type", "application/json");
        return $this;
    }

    /**
     * 设置请求超时时间
     *
     * @param int $milliseconds 超时时间(毫秒)
     * @return HttpRequest 返回当前实例以支持链式调用
     */
    public function timeout($milliseconds)
    {
        $this->timeoutMs = (int)$milliseconds;
        return $this;
    }

    /**
     * 执行HTTP请求
     *
     * @return HttpResponse 返回HTTP响应对象
     * @throws HttpException 当cURL执行出错时抛出异常
     */
    public function execute()
    {
        $ch = curl_init();

        // 构建 headers 数组
        $headers = array();
        foreach ($this->headers as $key => $value) {
            $headers[] = $key . ': ' . $value;
        }

        $options = array(
            CURLOPT_URL            => $this->url,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => http_build_query($this->formParams),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => false,
            CURLOPT_HTTPHEADER     => $headers,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_FOLLOWLOCATION => true,
        );

        // 处理请求体数据
        if ($this->jsonBody !== null) {
            // 如果设置了 JSON 数据，使用 JSON 请求体
            $options[CURLOPT_POSTFIELDS] = json_encode($this->jsonBody, JSON_UNESCAPED_UNICODE);
        } elseif (!empty($this->formParams)) {
            // 否则使用表单参数
            $options[CURLOPT_POSTFIELDS] = http_build_query($this->formParams);
        }

        // 注意：PHP 5.6 + cURL 支持 CURLOPT_TIMEOUT_MS，但某些旧系统可能不支持
        // 为兼容性，我们同时设置秒级超时（向下取整）
        $timeoutSec = floor($this->timeoutMs / 1000);
        if ($timeoutSec < 1) {
            $timeoutSec = 1; // 至少 1 秒
        }
        $options[CURLOPT_TIMEOUT] = $timeoutSec;

        // 尝试设置毫秒超时（如果支持）
        if (defined('CURLOPT_TIMEOUT_MS')) {
            $options[CURLOPT_TIMEOUT_MS] = $this->timeoutMs;
        }

        curl_setopt_array($ch, $options);

        $body = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);

        curl_close($ch);

        if ($error) {
            throw new HttpException("cURL Error: " . $error);
        }

        return new HttpResponse($body, $httpCode);
    }

}
